/*!
 * \file      eeprom-board.h
 *
 * \brief     Target board EEPROM driver implementation
 *
 * \copyright Revised BSD License, see section \ref LICENSE.
 *
 * \code
 *                ______                              _
 *               / _____)             _              | |
 *              ( (____  _____ ____ _| |_ _____  ____| |__
 *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 *               _____) ) ____| | | || |_| ____( (___| | | |
 *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
 *              (C)2013-2017 Semtech
 *
 * \endcode
 *
 * \author    Miguel Luis ( Semtech )
 *
 * \author    Gregory Cristian ( Semtech )
 */

#include "n32l43x_flash.h"
#include "eeprom-board.h"

#define EEPROM_RAM

#define FLASH_PAGE_SIZE                 ((uint16_t)0x800)
#define FLASH_EEPROM_SIZE               FLASH_PAGE_SIZE
#ifndef EEPROM_RAM
#define FLASH_EEPROM_BEGIN_ADDR         (0x8020000 - FLASH_EEPROM_SIZE)
#else
static uint8_t eeprom_ram_addr[FLASH_EEPROM_SIZE];
#define FLASH_EEPROM_BEGIN_ADDR         ((uint32_t)eeprom_ram_addr)
#endif

/*!
 * Writes the given buffer to the EEPROM at the specified address.
 *
 * \param[IN] addr EEPROM address to write to
 * \param[IN] buffer Pointer to the buffer to be written.
 * \param[IN] size Size of the buffer to be written.
 * \retval status [LMN_STATUS_OK, LMN_STATUS_ERROR]
 */
static __align(4) uint32_t eepromBuff[FLASH_PAGE_SIZE/4];
LmnStatus_t EepromMcuWriteBuffer( uint16_t addr, uint8_t *buffer, uint16_t size )
{
    if((addr + size) >= FLASH_EEPROM_SIZE) {
        return LMN_STATUS_ERROR;
    }
    const uint8_t *src = (uint8_t *)(FLASH_EEPROM_BEGIN_ADDR);
    uint8_t *dst = (uint8_t *)eepromBuff;
    FLASH_STS ret;
    memcpy1(dst, src, FLASH_PAGE_SIZE);
    memcpy1(&dst[addr], buffer, size);
    #ifndef EEPROM_RAM
    FLASH_Unlock();
    ret = FLASH_EraseOnePage(FLASH_EEPROM_BEGIN_ADDR);
    if(FLASH_COMPL == ret) {
        for(int i=0; i<FLASH_PAGE_SIZE; i+=4) {
            ret = FLASH_ProgramWord(FLASH_EEPROM_BEGIN_ADDR+(i), eepromBuff[i>>2]);
            if(FLASH_COMPL != ret) {
                break;
            }
        }
    }
    FLASH_Lock();
    #else
    (void)ret;
    memcpy1((uint8_t *)FLASH_EEPROM_BEGIN_ADDR, (uint8_t *)eepromBuff, FLASH_PAGE_SIZE);
    #endif
    if(ret != FLASH_COMPL) {
        return LMN_STATUS_ERROR;
    }
    return LMN_STATUS_OK;
}

/*!
 * Reads the EEPROM at the specified address to the given buffer.
 *
 * \param[IN] addr EEPROM address to read from
 * \param[OUT] buffer Pointer to the buffer to be written with read data.
 * \param[IN] size Size of the buffer to be read.
 * \retval status [LMN_STATUS_OK, LMN_STATUS_ERROR]
 */
LmnStatus_t EepromMcuReadBuffer( uint16_t addr, uint8_t *buffer, uint16_t size )
{
    if((addr + size) >= FLASH_EEPROM_SIZE) {
        return LMN_STATUS_ERROR;
    }
    const uint8_t *src = (uint8_t *)(FLASH_EEPROM_BEGIN_ADDR + addr);
    memcpy1(buffer, src, size);
    return LMN_STATUS_OK;
}

static uint8_t eeprom_addr = 0;
/*!
 * Sets the device address.
 *
 * \remark Useful for I2C external EEPROMS
 *
 * \param[IN] addr External EEPROM address
 */
void EepromMcuSetDeviceAddr( uint8_t addr )
{
    eeprom_addr = addr;
}

/*!
 * Gets the current device address.
 *
 * \remark Useful for I2C external EEPROMS
 *
 * \retval addr External EEPROM address
 */
LmnStatus_t EepromMcuGetDeviceAddr( void )
{
    return (LmnStatus_t)eeprom_addr;
}


